﻿/*********************************************************

    Copyright (c) Microsoft. All rights reserved.
    This code is licensed under the Microsoft Public License.
    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.

*********************************************************/
using System;
using System.Windows;
using System.Windows.Controls;
using SL_OData_Explorer.ViewModels;
using SL_OData_Explorer.Windows;
using OData.Silverlight;
using SL_OData_Explorer.Utilities;
using SL_OData_Explorer.Convertors;
using System.Linq;
using System.Collections.Generic;
using System.Data.Services.Client;
using System.Net;
using System.IO;
using System.Windows.Browser;
using System.Security;

namespace SL_OData_Explorer.Views
{
    public partial class ODataExplorer : UserControl
    {
        string workspaceName;
        MetadataUtility metadataUtility;
        QueryUtility queryUtility;
        EntityType CurrentEntityType;
        OData.Silverlight.EntitySet CurrentEntitySet;
        DictionaryConverter dictionaryConverter;
        BusyIndicatorViewModel busyViewModel;
        QueryBarViewModel queryBarViewModel;
        IAsyncResult CurrentQueryResult;
        ApplicationSettings appSettings;
        ScreenResolutionViewModel screenResolution;
        public ODataExplorer()
        {
            InitializeComponent();


            Application.Current.Host.Content.Resized += new EventHandler(Content_Resized);
            SetupScreenResolution();
            InitializeSavedWorkspaces();
            dictionaryConverter = this.Resources["dictionaryConverter"] as DictionaryConverter;
            busyViewModel = this.Resources["busyIndicatorModel"] as BusyIndicatorViewModel;
            appSettings = this.Resources["appSettingsViewModel"] as ApplicationSettings;
            queryBarViewModel = this.Resources["queryBarViewModel"] as QueryBarViewModel;
            ApplicationSettings.Current = appSettings;

            queryUtility = new QueryUtility();
            metadataUtility = new MetadataUtility();
            SetupViewModel();
            btnInstallApp.Visibility = App.Current.InstallState == InstallState.NotInstalled ? Visibility.Visible : Visibility.Collapsed;

            lbEntitySets.SelectionChanged += new SelectionChangedEventHandler(lbEntitySets_SelectionChanged);
            queryUtility.OnQueryBegin += new QueryUtility.OnQueryBeginDelegate(queryUtility_OnQueryBegin);
            queryUtility.OnQueryCompleted += new QueryUtility.OnQueryCompletedDelegate(queryUtility_OnQueryCompleted);

            metadataUtility.ParseComplete += new MetadataUtility.ParseCompleteDelegate(metadataUtility_ParseComplete);
            if (!UriContainsServiceUri())
            {
                ShowNewWorkspacePrompt();
            }

            queryBarViewModel.IsValid = (uriValueInTextBox) =>
            {
                if (string.IsNullOrEmpty(uriValueInTextBox))
                {
                    return false;
                }
                Uri uriInstance = new Uri(uriValueInTextBox, UriKind.RelativeOrAbsolute);
                //If the  URI in the text box is not an absolute URI , then convert it into an absolute URI
                if (!uriInstance.IsAbsoluteUri)
                {
                    uriInstance = new Uri(String.Format("{0}/{1}", App.MDViewModel.CurrentModel.ServiceDocument.BaseUri.OriginalString.TrimEnd('/'), uriValueInTextBox.TrimStart('/')));
                }
                string pathToSet = uriInstance.GetComponents(UriComponents.Path, UriFormat.Unescaped);
                string setName = pathToSet.Split('/').LastOrDefault();
                setName = setName.Split('(')[0];
                if (App.MDViewModel.CurrentModel.FirstContainerWithSets.EntitySets.Any(set => setName == set.SetName))
                {
                    return true;
                }
                return false;
            };
        }

        private void SetupViewModel()
        {
            App.MDViewModel.ModelSelectionChanged += new MetadataViewModel.OnModelChanged(MDViewModel_ModelSelectionChanged);
            lbServices.DataContext = App.MDViewModel;
            lbEntitySets.DataContext = App.MDViewModel;
            pbQueryInProgress.DataContext = busyViewModel;
        }

        MetadataParser mdataParser = null;
        ODataWorkspace currentWorkspaceWithoutMetadata = null;
        void MDViewModel_ModelSelectionChanged(EDMModel newWorkspace)
        {
            if (newWorkspace.DataServices == null)
            {
                currentWorkspaceWithoutMetadata = App.MDViewModel.Workspaces.FirstOrDefault(workspace => workspace.Name == newWorkspace.Name);
                pbMetadataDownloadInProgress.IsBusy = true;
                metadataUtility.StartMetadataProcessing(newWorkspace.DataServiceUri, newWorkspace.Name, false);
            }
        }

        void mdataParser_ParseComplete(EDMModel model, Exception Error)
        {
            if (Error == null)
            {
                currentWorkspaceWithoutMetadata.Model = model;
            }
            else
            {
                UIUtility.ShowMessageBox<UseInformation>(Error.Message);
            }
        }

        private void InitializeSavedWorkspaces()
        {
            var savedWorkspaces = IsolatedStorageUtility.GetWorkspacesFromStorage();
            foreach (var workspace in savedWorkspaces)
            {
                App.MDViewModel.Workspaces.Add(new ODataWorkspace()
                {
                    Name = workspace.Key,
                    Model = new EDMModel()
                    {
                        Name = workspace.Key,
                        DataServiceUri = workspace.Value
                    }
                });
            }
        }

        void Content_Resized(object sender, EventArgs e)
        {
            SetupScreenResolution();
        }

        private void SetupScreenResolution()
        {
            screenResolution = this.Resources["screenResolution"] as ScreenResolutionViewModel;
            screenResolution.TabMaxHeight = Application.Current.Host.Content.ActualHeight * 0.85;
            screenResolution.GridMaxHeight = Application.Current.Host.Content.ActualHeight * 0.7;

            screenResolution.TabMaxWidth = Application.Current.Host.Content.ActualWidth * 0.6;
            screenResolution.GridMaxWidth = Application.Current.Host.Content.ActualWidth * 0.58;
            screenResolution.RawViewMaxHeight = Application.Current.Host.Content.ActualHeight * 0.35;
        }

        void queryUtility_OnQueryBegin(IAsyncResult currentQueryResult)
        {
            CurrentQueryResult = currentQueryResult;
            lbEntitySets.IsEnabled = false;
            btnBuildQuery.IsEnabled = CurrentEntityType
                    .Properties
                    .Any(property => property.PropertyType == typeof(string) || property.PropertyType == typeof(DateTime) || property.PropertyType == typeof(int));
        }



        void metadataUtility_ParseComplete(EDMModel model, Exception Error)
        {
            if (Error != null)
            {
                UIUtility.ShowMessageBox<UseInformation>(Error.GetBaseException().Message);
            }
            else
            {
                if (currentWorkspaceWithoutMetadata != null)
                {
                    currentWorkspaceWithoutMetadata.Model = model;
                    currentWorkspaceWithoutMetadata = null;
                }
            }
            pbMetadataDownloadInProgress.IsBusy = false;
        }


        private bool UriContainsServiceUri()
        {
            if (App.Current.IsRunningOutOfBrowser)
            {
                return false;
            }
            if (HtmlPage.IsEnabled && System.Windows.Browser.HtmlPage.Document.DocumentUri.Query.Length > 0)
            {
                string queryString = System.Windows.Browser.HtmlPage.Document.DocumentUri.Query.Replace("?", "");
                string dataServiceUri = queryString.Replace("serviceUri=", "");
                workspaceName = "Test Workspace";
                pbMetadataDownloadInProgress.IsBusy = true;
                metadataUtility.StartMetadataProcessing(dataServiceUri, workspaceName);
                return true;
            }
            return false;

        }

        private void ShowNewWorkspacePrompt()
        {
            Dispatcher.BeginInvoke(
                () =>
                {
                    NewWorkspacePrompt newWorkspacePrompt = new NewWorkspacePrompt();
                    newWorkspacePrompt.Show();
                }
                );
        }


        void queryUtility_OnQueryCompleted(System.Data.Services.Client.QueryOperationResponse Response, Exception Error)
        {
            lbEntitySets.IsEnabled = true;
            if (Error == null)
            {

                dgEntitySet.ItemsSource = ((IEnumerable<ODataEntity>)Response).ToList();
                DataServiceQueryContinuation continuationToken = Response.GetContinuation();
                hlLoadNextPage.Visibility = continuationToken != null ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;
                hlLoadNextPage.Tag = continuationToken;
                if (continuationToken != null)
                {
                    ToolTipService.SetToolTip(hlLoadNextPage, continuationToken.NextLinkUri.OriginalString);
                }
                busyViewModel.IsBusy = false;
            }
            else
            {
                UIUtility.ShowMessageBox<UseInformation>(Error.GetBaseException().Message);
            }
            busyViewModel.IsBusy = false;
        }

        void lbEntitySets_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (lbEntitySets.SelectedItem != null && lbEntitySets.SelectedItem is OData.Silverlight.EntitySet)
            {
                CurrentEntitySet = lbEntitySets.SelectedItem as OData.Silverlight.EntitySet;
                queryBarViewModel.CurrentURI = String.Format("/{0}", CurrentEntitySet.SetName);
                CurrentEntityType = App.MDViewModel.CurrentModel.GetEntityType(CurrentEntitySet);

                busyViewModel.BusyMessage = String.Format("Loading entity set \"{0}\"", CurrentEntitySet.SetName);
                busyViewModel.IsBusy = true;

                queryUtility.DoGET(CurrentEntitySet);
                UIUtility.GenerateGridTemplate(dgEntitySet, lbEntitySets.SelectedItem as OData.Silverlight.EntitySet,
                    queryUtility.ServiceContext, dictionaryConverter,
                    (Action<ODataEntity, NavigationProperty>)LoadNavigationProperty);
                DownloadRawContent();
            }
        }

        private void CancelRunningQuery(object sender, RoutedEventArgs eventArgs)
        {
            if (CurrentQueryResult != null)
            {
                queryUtility.CancelRunningQuery(CurrentQueryResult);
                busyViewModel.IsBusy = false;
            }
        }
        private void LoadNavigationProperty(ODataEntity oDataEntity, NavigationProperty navProperty)
        {
            EntityType associatedType = App.MDViewModel.CurrentModel.GetEntityType(navProperty.To);
            if (associatedType == null)
            {
                associatedType = App.MDViewModel.CurrentModel.GetEntityType(navProperty.TypeName);
            }
            var entitySet = App.MDViewModel.CurrentModel.GetEntitySet(associatedType);

            UIUtility.GenerateGridTemplate(dgEntitySet, entitySet,
                queryUtility.ServiceContext, dictionaryConverter,
                (Action<ODataEntity, NavigationProperty>)LoadNavigationProperty);


            Uri navPropertyLink = oDataEntity.NavigationProperties[navProperty.Name];
            busyViewModel.IsBusy = true;
            busyViewModel.BusyMessage = String.Format("Loading Navigation Property :{0}", navProperty.Name);
            CurrentEntityType = associatedType;
            CurrentEntitySet = entitySet;
            queryUtility.ServiceContext.currentEntityType = associatedType;
            queryUtility.DoGET(entitySet, navPropertyLink);
            queryBarViewModel.CurrentURI = navPropertyLink.OriginalString.Replace(queryUtility.ServiceContext.BaseUri.OriginalString, "");
        }

        private void hlAddNewService_Click(object sender, RoutedEventArgs e)
        {
            NewWorkspacePrompt newWorkspace = new NewWorkspacePrompt();
            newWorkspace.Show();
        }


        private void CopyRawView(object sender, RoutedEventArgs e)
        {
            try
            {
                Clipboard.SetText(txtResponseText.Text);
            }
            catch (SecurityException securityException)
            {
            }
        }
        private void StartMetadataProcessing(string dataServiceUri)
        {
            metadataUtility.StartMetadataProcessing(dataServiceUri, workspaceName);
        }

        private void lbServices_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (lbServices.SelectedItem != null)
            {
                ODataWorkspace workspace = lbServices.SelectedItem as ODataWorkspace;
                if (workspace != null)
                {
                    App.MDViewModel.CurrentModel = workspace.Model;
                }
            }
        }

        private void btnBuildQuery_Click(object sender, RoutedEventArgs e)
        {
            if (!queryBarValidationErrorSummary.HasErrors && CurrentEntityType != null)
            {
                ODataQueryBuilder queryBuilder = new ODataQueryBuilder(CurrentEntityType, queryBarViewModel.CurrentURI);
                queryBuilder.Show();
                queryBuilder.Closed += new EventHandler(queryBuilder_Closed);
            }
        }

        void queryBuilder_Closed(object sender, EventArgs e)
        {
            ODataQueryBuilder queryBuilder = sender as ODataQueryBuilder;
            queryBarViewModel.CurrentURI = queryBuilder.txtCurrentUri.Text;
        }

        private void hlExecuteQuery_Click(object sender, RoutedEventArgs e)
        {
            txtServiceUri.UpdateBindingSource(TextBox.TextProperty);
            ExecuteUserQuery();
        }

        private void ExecuteUserQuery()
        {
            if (!queryBarValidationErrorSummary.HasErrors)
            {
                queryUtility.DoGET(CurrentEntitySet, new Uri(queryBarViewModel.CurrentURI, UriKind.RelativeOrAbsolute));
            }
        }

        private void Image_ImageFailed(object sender, ExceptionRoutedEventArgs e)
        {

        }
        IODataRequestCreator RequestCreator;
        private void GetRawView_Click(object sender, RoutedEventArgs e)
        {
            DownloadRawContent();
        }

        private void DownloadRawContent()
        {
            string strAcceptType = cbAcceptType.SelectedItem != null ? ((ComboBoxItem)cbAcceptType.SelectedItem).Content.ToString() : "ATOM";
            switch (strAcceptType)
            {
                case "JSON":
                    RequestCreator = new JSONRequestCreator();
                    break;
                case "ATOM":
                    RequestCreator = new AtomRequestCreator();
                    break;
            }
            Uri currentURI = new Uri(Uri.EscapeDataString(queryBarViewModel.CurrentURI), UriKind.RelativeOrAbsolute);
            if (!currentURI.IsAbsoluteUri)
            {
                currentURI = new Uri(String.Format("{0}/{1}", App.MDViewModel.CurrentModel.DataServiceUri.TrimEnd('/'), queryBarViewModel.CurrentURI.TrimStart('/')));
            }
            WebRequest GETRequest = RequestCreator.CreateGET(currentURI);
            GETRequest.BeginGetResponse(
                (asResult) =>
                {
                    Dispatcher.BeginInvoke(
                        () =>
                        {
                            WebResponse GETResponse = GETRequest.EndGetResponse(asResult);
                            List<KeyValuePair<string, string>> Headers = new List<KeyValuePair<string, string>>();
                            foreach (var header in GETResponse.Headers)
                            {
                                Headers.Add(new KeyValuePair<string, string>(header.ToString(), GETResponse.Headers[header.ToString()].ToString()));
                            }
                            icHeaders.ItemsSource = Headers;
                            StreamReader textResponseReader = new StreamReader(GETResponse.GetResponseStream());
                            txtResponseText.Text = textResponseReader.ReadToEnd();
                        }
                        );
                }, null);
        }

        private void txtServiceUri_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
        {
            txtServiceUri.UpdateBindingSource(TextBox.TextProperty);
            if (e.Key == System.Windows.Input.Key.Enter && queryBarViewModel.CurrentURI.Length > 0)
            {
                ExecuteUserQuery();
            }
        }

        private void LoadNextPage(object sender, RoutedEventArgs e)
        {

            DataServiceQueryContinuation continuationToken = ((Button)(sender)).Tag as DataServiceQueryContinuation;
            if (continuationToken != null)
            {
                busyViewModel.IsBusy = true;
                queryBarViewModel.CurrentURI = continuationToken.NextLinkUri.OriginalString;
                queryUtility.DoGET(CurrentEntitySet, continuationToken.NextLinkUri);
            }
        }

        private void InstallApplication(object sender, RoutedEventArgs e)
        {
            if (App.Current.InstallState == InstallState.NotInstalled)
            {
                App.Current.Install();
                btnInstallApp.Visibility = Visibility.Collapsed;
            }
        }

        private void hlDeleteService_Click(object sender, RoutedEventArgs e)
        {
            ODataWorkspace selectedWorkspace = lbServices.SelectedItem as ODataWorkspace;
            if (selectedWorkspace != null)
            {
                App.MDViewModel.Workspaces.Remove(selectedWorkspace);
                IsolatedStorageUtility.RemoveWorkspaceFromStorage(selectedWorkspace.Name, selectedWorkspace.Model.DataServiceUri);
                if (App.MDViewModel.Workspaces.Count == 0)
                {
                    ShowNewWorkspacePrompt();
                }
            }
        }


    }
}
